#include "cybertron/node/TrafficActorHotArea.hpp"
#include "cybertron/core/Log.hpp"
#ifdef CYBERTRON_LINUX
#include <cstring>
#endif
#include "Node/HotArea.pb.h"
#include "TrafficActorHotAreaRedisViewer.hpp"

CYBERTRON_BEGIN

CybertronCoreAPI TrafficActorHotArea::TrafficActorHotArea()
{
	mViewer = nullptr;
	zeroMembers();
}

CybertronCoreAPI TrafficActorHotArea::~TrafficActorHotArea()
{
	clearThis();
}

void TrafficActorHotArea::clearThis()
{
	mActors.clear();
	mActorIdSet.clear();

	zeroMembers();
}

void TrafficActorHotArea::zeroMembers()
{
	mId = -1;
	mWorldPosition = vec3(0, 0, 0);
	mWorldRotition = vec3(0, 0, 0);
	mRadius = 0;
	mRadiusSq = 0;
	mpActors = nullptr;
	mNumActors = 0;
	mActorMaxId = 0;

	mActors.clear();
	mActorIdSet.clear();

	if (mViewer)
	{
		delete mViewer;
		mViewer = nullptr;
	}
}

CybertronCoreAPI bool TrafficActorHotArea::create(
	int id, 
	const vec3& position, 
	const vec3& rotition,
	float radius)
{
	clear();

	mId = id;
	mWorldPosition = position;
	mWorldRotition = rotition;
	mRadius = radius;
	mRadiusSq = radius * radius;

	mActors.reserve(NumActorsReserved);

	return true;
}

CybertronCoreAPI void TrafficActorHotArea::clear()
{
	clearThis();
}

CybertronCoreAPI bool TrafficActorHotArea::isInRange(
	const vec3& position) const
{
	vec3 delta = position - mWorldPosition;
	return dot(delta, delta) < mRadiusSq;
}

CybertronCoreAPI bool TrafficActorHotArea::hasActor(int actorId) const
{
	if (actorId < 0 || actorId >= mActorMaxId)
	{
		return false;
	}

	return mpActors[actorId].getRuntimeId() >= 0;
}

CybertronCoreAPI void TrafficActorHotArea::deleteAllActors()
{
	mActors.clear();
	mActorIdSet.clear();

	mNumActors = 0;
}

CybertronCoreAPI void TrafficActorHotArea::createActor(
	int actorId, 
	HotArea::EActorType actorType,
	int assetId, 
	const vec3& size)
{
	//logEvent("TrafficActorHotArea::createActor %d", actorId);
	if (actorId >= (int)(mActors.size()))
	{
		mActors.resize((size_t)actorId + 1);
		mpActors = mActors.data();
		mActorMaxId = (int)mActors.size();
	}
	mActorIdSet.insert(actorId);
	mNumActors = (int)(mActorIdSet.size());
	auto& actor = mpActors[actorId];
	actor.setRuntimeId(actorId);
	actor.setActorType(actorType);
	actor.setAssetId(assetId);
	actor.setObjectSpaceSize(size);
	if (mViewer) mViewer->UpdateActor(mId, &actor);
}

CybertronCoreAPI void TrafficActorHotArea::deleteActor(int actorId)
{
	//logEvent("TrafficActorHotArea::deleteActor %d", actorId);
	if (actorId < 0 || actorId >= mActorMaxId)
	{
		return;
	}

	auto& actor = mpActors[actorId];
	actor.clear();
	mActorIdSet.erase(actorId);
	mNumActors = (int)mActorIdSet.size();
}

CybertronCoreAPI void TrafficActorHotArea::updateActorPosition(
	int actorId, 
	const vec3& position)
{
	if (actorId < 0 || actorId >= mActorMaxId)
	{
		return;
	}

	mpActors[actorId].setWorldPosition(position);
	if (mViewer) mViewer->UpdateActor(mId, &mpActors[actorId]);
}

CybertronCoreAPI void TrafficActorHotArea::updateActorPositionRotation(
	int actorId, 
	const vec3& position, 
	const vec3& rotation)
{
	if (actorId < 0 || actorId >= mActorMaxId)
	{
		return;
	}

	mpActors[actorId].setWorldPosition(position);
	mpActors[actorId].setWorldRotation(rotation);
	if (mViewer) mViewer->UpdateActor(mId, &mpActors[actorId]);
}

CybertronCoreAPI void TrafficActorHotArea::updateActorFlags(
	int actorId, 
	std::uint32_t flags)
{
	if (actorId < 0 || actorId >= mActorMaxId)
	{
		return;
	}

	mpActors[actorId].setFlags(flags);
	if (mViewer) mViewer->UpdateActor(mId, &mpActors[actorId]);
}

CybertronCoreAPI void TrafficActorHotArea::updateActorCustomProperties(
	int actorId, 
	const void* pBytes,
	size_t nBytes)
{
	if (actorId < 0 || actorId >= mActorMaxId)
	{
		return;
	}

	auto& bytes = mpActors[actorId].getCustomProperties();
	bytes.resize(nBytes);
	memcpy(bytes.data(), pBytes, nBytes);
	if (mViewer) mViewer->UpdateActor(mId, &mpActors[actorId]);
}

CybertronCoreAPI void TrafficActorHotArea::updateActorCustomProperties(
	int actorId,
	google::protobuf::MessageLite &protoMsg)
{
	if (actorId < 0 || actorId >= mActorMaxId)
	{
		return;
	}
	size_t msgBodyLength = protoMsg.ByteSizeLong();

	auto& bytes = mpActors[actorId].getCustomProperties();
	bytes.resize(msgBodyLength);
	protoMsg.SerializeToArray(bytes.data(), (int)msgBodyLength);
	if (mViewer) mViewer->UpdateActor(mId, &mpActors[actorId]);
}

CybertronCoreAPI void TrafficActorHotArea::applyChange(HotArea::FromAnyAddDeleteTrafficActors& change, int ownerId)
{
	int numAddedActorids = change.addedactorids_size();
	int numAddedActortypes = change.addedtypes_size();
	int numAddedAssetids = change.addedassetids_size();
	int numAddedSizes = change.addedpositionrotationsizes_size();

	int minimalNum = numAddedActorids > numAddedActortypes ? numAddedActortypes: numAddedActorids;
	if (numAddedAssetids < minimalNum)
	{
		minimalNum = numAddedAssetids;
	}
	if (numAddedSizes / 9 < minimalNum)
	{
		minimalNum = numAddedSizes / 9;
	}

	for (int i = 0; i < minimalNum; ++i)
	{
		int actorId = (int)change.addedactorids(i);
		HotArea::EActorType assetType = change.addedtypes(i);
		int assetId = (int)change.addedassetids(i);

		vec3 position;
		vec3 rotation;
		vec3 size;
		position.x = change.addedpositionrotationsizes(i * 9 + 0);
		position.y = change.addedpositionrotationsizes(i * 9 + 1);
		position.z = change.addedpositionrotationsizes(i * 9 + 2);
		rotation.x = change.addedpositionrotationsizes(i * 9 + 3);
		rotation.y = change.addedpositionrotationsizes(i * 9 + 4);
		rotation.z = change.addedpositionrotationsizes(i * 9 + 5);
		size.x = change.addedpositionrotationsizes(i * 9 + 6);
		size.y = change.addedpositionrotationsizes(i * 9 + 7);
		size.z = change.addedpositionrotationsizes(i * 9 + 8);

		createActor(actorId, assetType, assetId, size);
		updateActorPositionRotation(actorId, position, rotation);

		mpActors[actorId].setOwnerId(ownerId);
	}

	for (int i = 0; i < change.deletedactorids_size(); ++i)
	{
		int actorId = (int)change.deletedactorids(i);
		deleteActor(actorId);
	}
}

CybertronCoreAPI void TrafficActorHotArea::applyChange(HotArea::FromAnyUpdateTrafficActorPositionRotation& change)
{
	int numActors = change.actorids_size();
	int numPositionRotations = change.positionrotations_size();
	if (numPositionRotations / 6 < numActors)
	{
		numActors = numPositionRotations / 6;
	}

	for (int i = 0; i < numActors; ++i)
	{
		int actorId = (int)(change.actorids(i));
		vec3 position;
		vec3 rotation;
		position.x = change.positionrotations(i * 6 + 0);
		position.y = change.positionrotations(i * 6 + 1);
		position.z = change.positionrotations(i * 6 + 2);
		rotation.x = change.positionrotations(i * 6 + 3);
		rotation.y = change.positionrotations(i * 6 + 4);
		rotation.z = change.positionrotations(i * 6 + 5);
		updateActorPositionRotation(actorId, position, rotation);
	}
}

CybertronCoreAPI void TrafficActorHotArea::applyChange(HotArea::FromAnyUpdateTrafficActorFlags& change)
{
	int numActors = change.actorids_size();
	int numFlags = change.flags_size();

	if (numFlags < numActors)
	{
		numActors = numFlags;
	}

	for (int i = 0; i < numActors; ++i)
	{
		int actorId = (int)(change.actorids(i));
		std::uint32_t flag = change.flags(i);

		updateActorFlags(actorId, flag);
	}
}

CybertronCoreAPI void TrafficActorHotArea::applyChange(HotArea::FromAnyUpdateTrafficActorCustomProperties& change)
{
	int numActors = change.actorids_size();
	int numPropertySizes = change.custompropertysizes_size();

	if (numPropertySizes < numActors)
	{
		numActors = numPropertySizes;
	}

	const std::string& data = change.custompropertydatas();
	const unsigned char* pBytes = (const unsigned char*)(data.c_str());
	size_t numTotalBytes = data.size();
	size_t numRemainingBytes = numTotalBytes;

	for (int i = 0; i < numActors; ++i)
	{
		int actorId = (int)(change.actorids(i));
		std::uint32_t numBytes = change.custompropertysizes(i);
		if (numBytes > numRemainingBytes)
		{
			break;
		}

		updateActorCustomProperties(actorId, pBytes, numBytes);

		numRemainingBytes -= numBytes;
		pBytes += numBytes;
	}
}

void TrafficActorHotArea::enableViewer(int dbIndex)
{
	if (mViewer) delete mViewer;
	mViewer = new TrafficActorHotAreaRedisViewer();
	mViewer->Init("127.0.0.1", 6379, dbIndex);
	mViewer->RefreshAll(this);
}

CYBERTRON_END
